#include "bs_app.h"
#include "dev_flash.h"
#include <string.h>
#define LOG_TAG        "[file]"
#include "log.h"
#define fop_jif                           (bs_jiffies)
// 异步读超时：ms
#define FILE_REAR_TIME_OUT                80

// 预读块大小
#define FILE_AREAD_BLOCK_SHIFT                   10
#define FILE_AREAD_BLOCK_SIZE                   (1<<FILE_AREAD_BLOCK_SHIFT)


static uint8_t temp_read_buffer[FILE_BLOCK_SIZE];
void bs_sf_make_request(bs_sf_request_t *bd, int cmd);

/*!
    \brief
    \param[in]  none
    \param[out] none
    \retval     none
*/
int f2_open(bs_file_t *fp)
{
    fp->f_pos = 0;
    fp->f_buf_end = fp->f_buf_base + FILE_BLOCK_SIZE;
    fp->f_write_ptr = fp->f_buf_base;
    fp->state = 0;
    fp->flag = 0;
    fp->f_seg = 0;
    memset(&fp->f_buf_desc, 0, sizeof(fp->f_buf_desc));
    return 0;
}

/*!
    \brief
    \param[in]  none
    \param[out] none
    \retval     none
*/
int f2_close(bs_file_t *fp)
{
    f2_flush(fp);
    return 0;
}


/*!
    \brief
    \param[in]  none
    \param[out] none
    \retval     none
*/
int f2_flush(bs_file_t *fp)
{
    uint32_t status = 0;
    uint32_t addr = fp->f_pos;
    uint32_t count = fp->f_write_ptr - fp->f_buf_base;

    if(count > 0) {
        spi_flash_sector_erase(addr);
        spi_flash_buffer_write(fp->f_buf_base, addr, count);
        // 回读 对比
        spi_flash_buffer_read(temp_read_buffer, addr, count);
        for (int i=0; i<count; i++)
        {
            if(temp_read_buffer[i] != fp->f_buf_base[i]) {
                status = 1;
                fp->flag = 0xFFFF;
                while(1){;}
            }
        }
    }
    return status;
}


/*!
    \brief
    \param[in]  none
    \param[out] none
    \retval     none
*/
int f2_write(bs_file_t *fp, void *buf, uint32_t len)
{
    // 每次写入 4k 须扇区对齐
    uint8_t *s = (uint8_t *)buf;
    uint32_t blk_size = FILE_BLOCK_SIZE;
    uint32_t status=0;
    uint32_t written;
    if (len<=0) return 1;

    while(len > 0) {
        written = BS_MIN(len, fp->f_buf_end - fp->f_write_ptr);
        if (written > 0) {
            memcpy(fp->f_write_ptr, s, written);
            fp->f_write_ptr += written;
            s += written;
            len -= written;
        }
        if (fp->f_buf_end <= fp->f_write_ptr) {
            status = f2_flush(fp);
            fp->f_pos += blk_size;
            fp->f_write_ptr = fp->f_buf_base;
        }
    }
    return status;
}

// bs_writer_ctx_t *ctx = (bs_writer_ctx_t *)fp->f_owner;

/*!
    \brief 同步读取 阻塞模式
    \param[in]  none
    \param[out] none
    \retval     0: success, -1:failed
*/
int f2_read(bs_file_t *fp, void *buf, uint32_t len)
{
    int status = 1;
    // TODO
    while (status == 1)
    {
        status = f2_bread(fp, buf, len);
    }
    return status;
}


/*!
    \brief 异步读取
    \param[in]  none
    \param[out] none
    \retval     0: success, 1: busy, -1: failed
*/
// 如何判断缓存有效？ 如何让buffer无效：1. 没有读过数据, 2. 上一次读过数据
int f2_bread2(bs_file_t *fp, void *buf, uint32_t len)
{
    int read, offset, count, nr;
    int status = 1;
    bs_sf_request_t *bd = &fp->f_buf_desc[0];

    if (fp->state == 0) {
        // 检查参数有效性，文件是否结束,
        if (len == 0) return 0;
        fp->f_user_left = len;
        fp->f_user_buf = buf;
        fp->state = 1;
    }
    // 当请求长度小于 2*4096，那么此循环体最多执行两次
    while(1) {
        if (fp->state == 1) {
            // 根据当前文件指针，计算逻辑块，判断块缓冲是否有效：如果无效，提交读扇区请求，所以这里是异步的，
            // 这里是否需要先判断是否锁住？
            fp->state = 2;
            nr = fp->start_blk + (fp->f_pos)>>FILE_BLOCK_BITS;
            if (bd->b_update == 0 || bd->b_blocknr != nr) {
                bd->b_blocknr = nr;
                bd->b_addr = nr<<FILE_BLOCK_BITS;
                bd->b_data = fp->f_buf_base;
                bd->b_size = FILE_BLOCK_SIZE;
                bd->b_lock = 1;
                bd->b_update = 0;
                bs_sf_make_request(bd, 0);
                bd->timeout = fop_jif + FILE_REAR_TIME_OUT;
                break;
            }
        } else {
            // 等待直到超时，或者读取成功
            if (bd->b_lock != 0) {
                // 判断是否超时
                if (fop_jif > bd->timeout) {
                    status = -1;
                    fp->state = 0;
                    fp->flag |= BIT(4);
                }
                break;
            } else{
                // 读取成功：获取 base_buff 的偏移量
                offset = fp->f_pos & (FILE_BLOCK_SIZE - 1);
                count = BS_MIN(fp->f_user_left, FILE_BLOCK_SIZE - offset);
                
                fp->f_pos += count;
                read += count;
                memcpy(fp->f_user_buf, fp->f_buf_base+offset, count);
                fp->f_user_buf += count;
                fp->f_user_left -= count;

                // 继续读取
                fp->state = 1;
                if (fp->f_user_left == 0) {
                    // 读取成功，复位状态机
                    fp->state = 0;
                    status = 0;
                    break;
                }
            }
        }
    }
    return status;
}


/*!
    \brief 异步操作 判断上一步操作是否完成; 如果没有完成则需多次调用 f_read
    \param[in]  none
    \param[out] none
    \retval     0: success, 1: busy
*/
int f2_ioctl(bs_file_t *fp, int cmd) {
    int status = (fp->state == 0 ? 0: 1);
    (void)cmd;
    return status;
}


/*!
    \brief 异步读取，预读
    \param[in]  none
    \param[out] none
    \retval     0: success, 1: busy, -1: failed
*/
// 如何判断缓存有效？ 如何让buffer无效：1. 没有读过数据, 2. 上一次读过数据
// 这里每块的大小是 1kbyte，但是缓存区总大小仍然是 4kB
int f2_bread(bs_file_t *fp, void *buf, uint32_t len)
{
    int read, offset, count, nr, oppo;
    int status = 1;
    bs_sf_request_t *bd = &fp->f_buf_desc[fp->f_seg];

    if (fp->state == 0) {
        // 检查参数有效性，文件是否结束,
        if (len == 0) return 0;
        fp->f_user_left = len;
        fp->f_user_buf = buf;
        fp->state = 1;
    }
    // 当请求长度小于 2*4096，那么此循环体最多执行两次
    while(1) {
        if (fp->state == 1) {
            // 这里是否需要先判断是否锁住？// 直接判断当前是否有效
            if (bd->b_lock == 1) {
                LOGE("flash request block locked!\r\n");
            }
            fp->state = 2;
            nr = fp->start_blk*(1<<(FILE_BLOCK_BITS-FILE_AREAD_BLOCK_SHIFT)) + ((fp->f_pos)>>FILE_AREAD_BLOCK_SHIFT);
            if (bd->b_blocknr == nr && bd->b_update == 1) continue;
            // 当前缓冲块无效的时候，采取判断是否预读
            oppo = fp->f_seg ^ 0x01;
            bd = &fp->f_buf_desc[oppo];
            if (bd->b_blocknr == nr && (bd->b_update || bd->b_lock)) {
                // 返回数据，提交另一块
            } else {
                // 另一块也无效，说明两块都无效
                bd->b_blocknr = nr;
                bd->b_addr = nr<<FILE_AREAD_BLOCK_SHIFT;
                bd->b_data = fp->f_buf_base + oppo*FILE_AREAD_BLOCK_SIZE;
                bd->b_size = FILE_AREAD_BLOCK_SIZE;
                bd->b_lock = 1;
                bd->b_update = 0;
                bs_sf_make_request(bd, 0);
                bd->timeout = fop_jif + FILE_REAR_TIME_OUT*2;
            }
            //当前缓存无效, 提交当前块，作为预读
            bd = &fp->f_buf_desc[fp->f_seg];
            bd->b_blocknr = nr + 1;
            bd->b_addr = (nr+1)<<FILE_AREAD_BLOCK_SHIFT;
            bd->b_data = fp->f_buf_base + fp->f_seg*FILE_AREAD_BLOCK_SIZE;
            bd->b_size = FILE_AREAD_BLOCK_SIZE;
            bd->b_lock = 1;
            bd->b_update = 0;
            bs_sf_make_request(bd, 0);
            bd->timeout = fop_jif + FILE_REAR_TIME_OUT*2;
            fp->f_seg = oppo;
            continue; // 因为缓存可能有效，此时可以直接读取，故先不要退出循环
        } else {
            // 等待直到超时，或者读取成功
            if (bd->b_lock != 0) {
                // 判断是否超时
                if (fop_jif > bd->timeout) {
                    status = -1;
                    fp->state = 0;
                    fp->flag |= BIT(4);
                    LOGE("read spi flash timeout\r\n");
                }
                break;
            } else{
                // 读取成功：获取 base_buff 的偏移量
                // 这里要确保数据有效
                offset = fp->f_pos & (FILE_AREAD_BLOCK_SIZE - 1);
                count = BS_MIN(fp->f_user_left, FILE_AREAD_BLOCK_SIZE - offset);
                
                fp->f_pos += count;
                read += count;
                memcpy(fp->f_user_buf, fp->f_buf_base + fp->f_seg*FILE_AREAD_BLOCK_SIZE + offset, count);
                fp->f_user_buf += count;
                fp->f_user_left -= count;

                // 继续读取
                fp->state = 1;
                if (fp->f_user_left == 0) {
                    // 读取成功，复位状态机
                    fp->state = 0;
                    status = 0;
                    break;
                }
            }
        }
    }
    return status;
}
